using System;
using System.Windows;
using System.Windows.Data;

using Microsoft.Practices.Composite.Presentation.Commands;
using Microsoft.Practices.Composite.Presentation.Events;
using Microsoft.Practices.Unity;

using Supremacy.Annotations;
using Supremacy.Buildings;
using Supremacy.Client.Dialogs;
using Supremacy.Client.Events;
using Supremacy.Economy;

using System.Linq;

using Supremacy.Game;
using Supremacy.Orbitals;
using Supremacy.Tech;
using Supremacy.Universe;

using CompositeRegionManager = Microsoft.Practices.Composite.Presentation.Regions.RegionManager;

namespace Supremacy.Client.Views
{
    public class ColonyScreenPresenter
        : GameScreenPresenterBase<ColonyScreenPresentationModel, IColonyScreenView>, IColonyScreenPresenter
    {
        private readonly DelegateCommand<BuildProject> _addToPlanetaryBuildQueueCommand;
        private readonly DelegateCommand<BuildProject> _addToShipyardBuildQueueCommand;
        private readonly DelegateCommand<BuildQueueItem> _removeFromPlanetaryBuildQueueCommand;
        private readonly DelegateCommand<BuildQueueItem> _removeFromShipyardBuildQueueCommand;
        private readonly DelegateCommand<BuildProject> _cancelBuildProjectCommand;
        private readonly DelegateCommand<ProductionCategory> _activateFacilityCommand;
        private readonly DelegateCommand<ProductionCategory> _deactivateFacilityCommand;
        private readonly DelegateCommand<ProductionCategory> _scrapFacilityCommand;
        private readonly DelegateCommand<ProductionCategory> _unscrapFacilityCommand;
        private readonly DelegateCommand<object> _toggleBuildingScrapCommand;
        private readonly DelegateCommand<Building> _toggleBuildingIsActiveCommand;
        private readonly DelegateCommand<ShipyardBuildSlot> _toggleShipyardBuildSlotCommand;
        private readonly DelegateCommand<ShipyardBuildSlot> _selectShipBuildProjectCommand;
        private readonly DelegateCommand<Colony> _navigateToColonyCommand;
        private readonly DelegateCommand<object> _previousColonyCommand;
        private readonly DelegateCommand<object> _nextColonyCommand;

        private GameObjectID _newColonySelection;

        #region Constructors and Finalizers
        public ColonyScreenPresenter(
            [NotNull] IUnityContainer container,
            [NotNull] ColonyScreenPresentationModel model,
            [NotNull] IColonyScreenView view) : base(container, model, view)
        {
            this.Model.SelectedColonyChanged += OnSelectedColonyChanged;

            _addToPlanetaryBuildQueueCommand = new DelegateCommand<BuildProject>(
                this.ExecuteAddToPlanetaryBuildQueueCommand,
                this.CanExecuteAddToPlanetaryBuildQueueCommand);

            _addToShipyardBuildQueueCommand = new DelegateCommand<BuildProject>(
                this.ExecuteAddToShipyardBuildQueueCommand,
                this.CanExecuteAddToShipyardBuildQueueCommand);

            _removeFromPlanetaryBuildQueueCommand = new DelegateCommand<BuildQueueItem>(
                this.ExecuteRemoveFromPlanetaryBuildQueueCommand,
                this.CanExecuteRemoveFromPlanetaryBuildQueueCommand);

            _removeFromShipyardBuildQueueCommand = new DelegateCommand<BuildQueueItem>(
                this.ExecuteRemoveFromShipyardBuildQueueCommand,
                this.CanExecuteRemoveFromShipyardBuildQueueCommand);

            _cancelBuildProjectCommand = new DelegateCommand<BuildProject>(
                this.ExecuteCancelBuildProjectCommand,
                this.CanExecuteCancelBuildProjectCommand);

            _activateFacilityCommand = new DelegateCommand<ProductionCategory>(
                this.ExecuteActivateFacilityCommand,
                this.CanExecuteActivateFacilityCommand);

            _deactivateFacilityCommand = new DelegateCommand<ProductionCategory>(
                this.ExecuteDeactivateFacilityCommand,
                this.CanExecuteDeactivateFacilityCommand);

            _scrapFacilityCommand = new DelegateCommand<ProductionCategory>(
                this.ExecuteScrapFacilityCommand,
                this.CanExecuteScrapFacilityCommand);

            _unscrapFacilityCommand = new DelegateCommand<ProductionCategory>(
                this.ExecuteUnscrapFacilityCommand,
                this.CanExecuteUnscrapFacilityCommand);

            _toggleBuildingScrapCommand = new DelegateCommand<object>(
                this.ExecuteToggleBuildingScrapCommand,
                this.CanExecuteToggleBuildingScrapCommand);

            _toggleBuildingIsActiveCommand = new DelegateCommand<Building>(
                this.ExecuteToggleBuildingIsActiveCommand,
                this.CanExecuteToggleBuildingIsActiveCommand);
            
            _toggleShipyardBuildSlotCommand = new DelegateCommand<ShipyardBuildSlot>(
                this.ExecuteToggleShipyardBuildSlotCommand,
                this.CanExecuteToggleShipyardBuildSlotCommand);

            _selectShipBuildProjectCommand = new DelegateCommand<ShipyardBuildSlot>(
                this.ExecuteSelectShipBuildProjectCommand,
                this.CanExecuteSelectShipBuildProjectCommand);

            _navigateToColonyCommand = new DelegateCommand<Colony>(
                colony => _newColonySelection = (colony != null) ? colony.ObjectID : GameObjectID.InvalidID);

            _previousColonyCommand = new DelegateCommand<object>(this.ExecutePreviousColonyCommand);
            _nextColonyCommand = new DelegateCommand<object>(this.ExecuteNextColonyCommand);
        }

        private void ExecutePreviousColonyCommand(object _)
        {
            var colonies = this.Model.Colonies.ToList();
            var currentColony = this.Model.SelectedColony;

            var currentColonyIndex = colonies.IndexOf(currentColony);
            if (currentColonyIndex <= 0)
            {
                if (colonies.Count == 0)
                    return;

                this.Model.SelectedColony = colonies[colonies.Count - 1];
            }
            else
            {
                this.Model.SelectedColony = colonies[currentColonyIndex - 1];
            }
        }

        private void ExecuteNextColonyCommand(object _)
        {
            var colonies = this.Model.Colonies.ToList();
            var currentColony = this.Model.SelectedColony;

            var currentColonyIndex = colonies.IndexOf(currentColony);
            if ((currentColonyIndex == (colonies.Count - 1)) || (currentColonyIndex < 0))
                this.Model.SelectedColony = colonies[0];
            else
                this.Model.SelectedColony = colonies[currentColonyIndex + 1];
        }

        protected override void OnViewActivating()
        {
            var newColonySelection = _newColonySelection;
            if (!newColonySelection.IsValid)
            {
                this.Model.SelectedColony = this.ClientContext.LocalPlayerEmpire.HomeColony;
                return;
            }
            this.Model.SelectedColony = this.ClientContext.CurrentGame.Universe.Objects[newColonySelection] as Colony;
        }

        private bool CanExecuteToggleBuildingIsActiveCommand(Building building)
        {
            return (this.Model.SelectedColony != null);
        }

        private void ExecuteToggleBuildingIsActiveCommand(Building building)
        {
            var colony = this.Model.SelectedColony;
            if (colony == null)
                return;

            if (building.IsActive)
                colony.DeactivateBuilding(building);
            else
                colony.ActivateBuilding(building);

            this.PlayerOrderService.AddOrder(new UpdateBuildingOrder(building));
        }

        private bool CanExecuteToggleShipyardBuildSlotCommand(ShipyardBuildSlot buildSlot)
        {
            if (buildSlot == null)
                return false;

            var colony = this.Model.SelectedColony;
            if (colony == null || colony.Shipyard != buildSlot.Shipyard)
                return false;

            return true;
        }

        private void ExecuteToggleShipyardBuildSlotCommand(ShipyardBuildSlot buildSlot)
        {
            if (buildSlot == null)
                return;

            var colony = this.Model.SelectedColony;
            if (colony == null || colony.Shipyard != buildSlot.Shipyard)
                return;

            if (buildSlot.IsActive)
                colony.DeactivateShipyardBuildSlot(buildSlot);
            else
                colony.ActivateShipyardBuildSlot(buildSlot);

            this.PlayerOrderService.AddOrder(new ToggleShipyardBuildSlotOrder(buildSlot));
        }
        
        private bool CanExecuteSelectShipBuildProjectCommand(ShipyardBuildSlot buildSlot)
        {
            if (buildSlot == null)
                return false;

            var colony = this.Model.SelectedColony;
            if (colony == null || colony.Shipyard != buildSlot.Shipyard)
                return false;

            return buildSlot.IsActive && !buildSlot.HasProject;
        }

        private void ExecuteSelectShipBuildProjectCommand(ShipyardBuildSlot buildSlot)
        {
            if (buildSlot == null)
                return;

            var colony = this.Model.SelectedColony;
            if (colony == null || colony.Shipyard != buildSlot.Shipyard)
                return;

            if (!buildSlot.IsActive || buildSlot.HasProject)
                return;

            var view = new NewShipSelectionView(buildSlot);
            var statsViewModel = new TechObjectDesignViewModel();

            BindingOperations.SetBinding(
                statsViewModel,
                TechObjectDesignViewModel.DesignProperty,
                new Binding
                {
                    Source = view,
                    Path = new PropertyPath("SelectedBuildProject.BuildDesign")
                });

            view.AdditionalContent = statsViewModel;

            var result = view.ShowDialog();

            if (!result.HasValue || !result.Value)
                return;

            var project = view.SelectedBuildProject;
            if (project == null)
                return;

            buildSlot.Project = project;
            
            this.PlayerOrderService.AddOrder(new UpdateProductionOrder(buildSlot.Shipyard));
        }

        private bool CanExecuteToggleBuildingScrapCommand(object parameter)
        {
            var checkableParameter = parameter as ICheckableCommandParameter;
            if (checkableParameter != null)
            {
                var building = checkableParameter.InnerParameter as Building;
                if (building == null)
                {
                    checkableParameter.IsChecked = false;
                    return false;
                }
                checkableParameter.IsChecked = building.Scrap;
                checkableParameter.Handled = true;
            }
            else if (!(parameter is Building))
            {
                return false;
            }
            return (this.Model.SelectedColony != null);
        }

        private void ExecuteToggleBuildingScrapCommand(object parameter)
        {
            var building = parameter as Building;
            if (building != null)
            {
                building.Scrap = !building.Scrap;
            }
            else
            {
                var checkableParameter = parameter as ICheckableCommandParameter;
                if (checkableParameter == null)
                    return;
                
                building = checkableParameter.InnerParameter as Building;
                if (building == null)
                    return;

                checkableParameter.IsChecked = (building.Scrap = !building.Scrap);
                checkableParameter.Handled = true;
            }

            this.PlayerOrderService.AddOrder(new UpdateBuildingOrder(building));
        }

        private bool CanExecuteUnscrapFacilityCommand(ProductionCategory category)
        {
            return (this.Model.SelectedColony != null);
        }

        private void ExecuteUnscrapFacilityCommand(ProductionCategory category)
        {
            var colony = this.Model.SelectedColony;
            if (colony == null)
                return;

            var facilitiesToScrap = colony.GetScrappedFacilities(category);
            if (facilitiesToScrap == 0)
                return;

            colony.SetScrappedFacilities(category, --facilitiesToScrap);

            this.PlayerOrderService.AddOrder(new FacilityScrapOrder(colony));
        }

        private bool CanExecuteScrapFacilityCommand(ProductionCategory category)
        {
            return (this.Model.SelectedColony != null);
        }

        private void ExecuteScrapFacilityCommand(ProductionCategory category)
        {
            var colony = this.Model.SelectedColony;
            if (colony == null)
                return;

            var facilitiesToScrap = colony.GetScrappedFacilities(category);
            if (facilitiesToScrap >= colony.GetTotalFacilities(category))
                return;

            colony.SetScrappedFacilities(category, ++facilitiesToScrap);

            this.PlayerOrderService.AddOrder(new FacilityScrapOrder(colony));
        }

        private bool CanExecuteDeactivateFacilityCommand(ProductionCategory category)
        {
            return (this.Model.SelectedColony != null);
        }

        private void ExecuteDeactivateFacilityCommand(ProductionCategory category)
        {
            var colony = this.Model.SelectedColony;
            if (colony == null)
                return;

            colony.DeactivateFacility(category);

            this.PlayerOrderService.AddOrder(new SetColonyProductionOrder(colony));
        }

        private bool CanExecuteActivateFacilityCommand(ProductionCategory category)
        {
            return (this.Model.SelectedColony != null);
        }

        private void ExecuteActivateFacilityCommand(ProductionCategory category)
        {
            var colony = this.Model.SelectedColony;
            if (colony == null)
                return;

            colony.ActivateFacility(category);

            this.PlayerOrderService.AddOrder(new SetColonyProductionOrder(colony));
        }

        private void OnSelectedColonyChanged(object sender, EventArgs e)
        {
            if (!this.IsRunning)
                return;

            this.Model.Colonies = this.ClientContext.LocalPlayerEmpire.Colonies;

            UpdateBuildLists();

            var selectedColony = this.Model.SelectedColony;
            if (selectedColony != null)
            {
                var regionManager = CompositeRegionManager.GetRegionManager((DependencyObject)this.View);

                if (!regionManager.Regions.ContainsRegionWithName(CommonGameScreenRegions.PlanetsView))
                    CompositeRegionManager.UpdateRegions();

                if (regionManager.Regions.ContainsRegionWithName(CommonGameScreenRegions.PlanetsView))
                {
                    var planetsViewRegion = regionManager.Regions[CommonGameScreenRegions.PlanetsView];
                    planetsViewRegion.Context = selectedColony.Sector;
                }
            }

            InvalidateCommands();
        }

        private void UpdateBuildLists()
        {
            var selectedColony = this.Model.SelectedColony;
            if (selectedColony != null)
            {
                this.Model.PlanetaryBuildProjects = TechTreeHelper.GetBuildProjects(this.Model.SelectedColony);
                if (selectedColony.Shipyard != null)
                    this.Model.ShipyardBuildProjects = TechTreeHelper.GetShipyardBuildProjects(selectedColony.Shipyard);
                else
                    this.Model.ShipyardBuildProjects = Enumerable.Empty<BuildProject>();
            }
            else
            {
                this.Model.PlanetaryBuildProjects = Enumerable.Empty<BuildProject>();
            }
        }

        protected override void InvalidateCommands()
        {
            base.InvalidateCommands();

            _addToPlanetaryBuildQueueCommand.RaiseCanExecuteChanged();
            _addToShipyardBuildQueueCommand.RaiseCanExecuteChanged();
            _removeFromPlanetaryBuildQueueCommand.RaiseCanExecuteChanged();
            _removeFromShipyardBuildQueueCommand.RaiseCanExecuteChanged();
            _cancelBuildProjectCommand.RaiseCanExecuteChanged();
            _activateFacilityCommand.RaiseCanExecuteChanged();
            _deactivateFacilityCommand.RaiseCanExecuteChanged();
            _scrapFacilityCommand.RaiseCanExecuteChanged();
            _unscrapFacilityCommand.RaiseCanExecuteChanged();
            _toggleBuildingScrapCommand.RaiseCanExecuteChanged();
            _toggleBuildingIsActiveCommand.RaiseCanExecuteChanged();
            _toggleShipyardBuildSlotCommand.RaiseCanExecuteChanged();
            _selectShipBuildProjectCommand.RaiseCanExecuteChanged();
        }

        protected override void RegisterCommandAndEventHandlers()
        {
            base.RegisterCommandAndEventHandlers();

            this.Model.AddToPlanetaryBuildQueueCommand = _addToPlanetaryBuildQueueCommand;
            this.Model.AddToShipyardBuildQueueCommand = _addToShipyardBuildQueueCommand;
            this.Model.RemoveFromPlanetaryBuildQueueCommand = _removeFromPlanetaryBuildQueueCommand;
            this.Model.RemoveFromShipyardBuildQueueCommand = _removeFromShipyardBuildQueueCommand;
            this.Model.CancelBuildProjectCommand = _cancelBuildProjectCommand;
            this.Model.ScrapFacilityCommand = _scrapFacilityCommand;
            this.Model.UnscrapFacilityCommand = _unscrapFacilityCommand;
            this.Model.ActivateFacilityCommand = _activateFacilityCommand;
            this.Model.DeactivateFacilityCommand = _deactivateFacilityCommand;
            this.Model.ToggleBuildingIsActiveCommand = _toggleBuildingIsActiveCommand;
            this.Model.ToggleBuildingScrapCommand = _toggleBuildingScrapCommand;
            this.Model.ToggleShipyardBuildSlotCommand = _toggleShipyardBuildSlotCommand;
            this.Model.SelectShipBuildProjectCommand = _selectShipBuildProjectCommand;

            ColonyScreenCommands.ToggleBuildingScrapCommand.RegisterCommand(_toggleBuildingScrapCommand);
            ColonyScreenCommands.PreviousColonyCommand.RegisterCommand(_previousColonyCommand);
            ColonyScreenCommands.NextColonyCommand.RegisterCommand(_nextColonyCommand);

            this.NavigationCommands.NavigateToColony.RegisterCommand(_navigateToColonyCommand);

            ClientEvents.TurnStarted.Subscribe(OnTurnStarted, ThreadOption.UIThread);
        }

        private void OnTurnStarted(GameContextEventArgs args)
        {
            var selectedColony = this.Model.SelectedColony;
            if (selectedColony == null)
                this.Model.SelectedColony = this.ClientContext.LocalPlayerEmpire.HomeColony;
        }

        private bool CanExecuteCancelBuildProjectCommand(BuildProject project)
        {
            if (this.Model.SelectedColony == null)
                return false;

            if (project is ShipBuildProject)
                return (this.Model.SelectedColony.Shipyard != null);

            return true;
        }

        private void ExecuteCancelBuildProjectCommand([NotNull] BuildProject project)
        {
            if (project == null)
                throw new ArgumentNullException("project");

            var productionCenter = project.ProductionCenter;
            if (productionCenter == null)
                return;

            var buildSlot = productionCenter.BuildSlots.FirstOrDefault(o => o.Project == project);
            if (buildSlot == null)
                return;

            if (project.IsPartiallyComplete)
            {
                var confirmResult = MessageDialog.Show(
                    this.ResourceManager.GetString("CONFIRM_CANCEL_BUILD_HEADER"),
                    this.ResourceManager.GetString("CONFIRM_CANCEL_BUILD_MESSAGE"),
                    MessageDialogButtons.YesNo);

                if (confirmResult != MessageDialogResult.Yes)
                    return;
            }

            project.Cancel();
            productionCenter.ProcessQueue();

            this.PlayerOrderService.AddOrder(new UpdateProductionOrder(productionCenter));

            UpdateBuildLists();
        }

        private bool CanExecuteRemoveFromShipyardBuildQueueCommand(BuildQueueItem item)
        {
            return ((this.Model.SelectedColony != null) && (this.Model.SelectedColony.Shipyard != null));
        }

        private void ExecuteRemoveFromShipyardBuildQueueCommand(BuildQueueItem item)
        {
            var colony = this.Model.SelectedColony;
            if (colony == null)
                return;

            if (colony.Shipyard == null)
                return;

            RemoveItemFromBuildQueue(item, colony.Shipyard);
        }

        private bool CanExecuteRemoveFromPlanetaryBuildQueueCommand(BuildQueueItem item)
        {
            return (this.Model.SelectedColony != null);
        }

        private void ExecuteRemoveFromPlanetaryBuildQueueCommand(BuildQueueItem item)
        {
            var colony = this.Model.SelectedColony;
            if (colony == null)
                return;

            RemoveItemFromBuildQueue(item, colony);
        }

        private bool CanExecuteAddToShipyardBuildQueueCommand(BuildProject project)
        {
            return ((this.Model.SelectedColony != null) && (this.Model.SelectedColony.Shipyard != null));
        }

        private void ExecuteAddToShipyardBuildQueueCommand(BuildProject project)
        {
            var colony = this.Model.SelectedColony;
            if (colony == null)
                return;

            if (colony.Shipyard == null)
                return;

            AddProjectToBuildQueue(project, colony.Shipyard);
        }

        protected void RemoveItemFromBuildQueue([NotNull] BuildQueueItem item, [NotNull] IProductionCenter productionCenter)
        {
            if (item == null)
                throw new ArgumentNullException("item");
            if (productionCenter == null)
                throw new ArgumentNullException("productionCenter");

            if ((item.Count <= 1) || !item.DecrementCount())
                productionCenter.BuildQueue.Remove(item);

            this.PlayerOrderService.AddOrder(new UpdateProductionOrder(productionCenter));

            UpdateBuildLists();
        }

        protected void AddProjectToBuildQueue([NotNull] BuildProject project, [NotNull] IProductionCenter productionCenter)
        {
            if (project == null)
                throw new ArgumentNullException("project");
            if (productionCenter == null)
                throw new ArgumentNullException("productionCenter");

            var newItemAdded = true;
            var lastItemInQueue = productionCenter.BuildQueue.LastOrDefault();

            if ((lastItemInQueue != null) && project.IsEquivalent(lastItemInQueue.Project))
            {
                if (lastItemInQueue.IncrementCount())
                    newItemAdded = false;
            }

            if (newItemAdded)
            {
                productionCenter.BuildQueue.Add(new BuildQueueItem(project));
                productionCenter.ProcessQueue();
            }

            this.PlayerOrderService.AddOrder(new UpdateProductionOrder(productionCenter));

            if (productionCenter is Colony)
                this.Model.SelectedPlanetaryBuildProject = null;
            else if (productionCenter is Shipyard)
                this.Model.SelectedShipyardBuildProject = null;

            UpdateBuildLists();
        }

        protected override void UnregisterCommandAndEventHandlers()
        {
            base.UnregisterCommandAndEventHandlers();

            this.Model.AddToPlanetaryBuildQueueCommand = null;
            this.Model.AddToShipyardBuildQueueCommand = null;
            this.Model.RemoveFromPlanetaryBuildQueueCommand = null;
            this.Model.RemoveFromShipyardBuildQueueCommand = null;
            this.Model.CancelBuildProjectCommand = null;
            this.Model.ScrapFacilityCommand = null;
            this.Model.UnscrapFacilityCommand = null;
            this.Model.ActivateFacilityCommand = null;
            this.Model.DeactivateFacilityCommand = null;
            this.Model.ToggleBuildingIsActiveCommand = null;
            this.Model.ToggleBuildingScrapCommand = null;

            ColonyScreenCommands.ToggleBuildingScrapCommand.UnregisterCommand(_toggleBuildingScrapCommand);
            ColonyScreenCommands.PreviousColonyCommand.UnregisterCommand(_previousColonyCommand);
            ColonyScreenCommands.NextColonyCommand.UnregisterCommand(_nextColonyCommand);

            this.NavigationCommands.NavigateToColony.UnregisterCommand(_navigateToColonyCommand);

            ClientEvents.TurnStarted.Unsubscribe(OnTurnStarted);
        }

        private bool CanExecuteAddToPlanetaryBuildQueueCommand(BuildProject arg)
        {
            return (this.Model.SelectedColony != null);
        }

        private void ExecuteAddToPlanetaryBuildQueueCommand([NotNull] BuildProject project)
        {
            if (project == null)
                throw new ArgumentNullException("project");

            var colony = this.Model.SelectedColony;
            if (colony == null)
                return;

            AddProjectToBuildQueue(project, colony);
        }
        #endregion

        #region Overrides of GameScreenPresenterBase<ColonyScreenPresentationModel,IColonyScreenView>
        protected override string ViewName
        {
            get { return StandardGameScreens.ColonyScreen; }
        }
        #endregion
    }
}